%{
let y = Ast.Fun("f",Ast.App(Ast.Fun("x",Ast.App(Ast.Var "f",Ast.App(Ast.Var "x",Ast.Var "x"))),Ast.Fun("x",Ast.App(Ast.Var "f",Ast.App(Ast.Var "x",Ast.Var "x")))))
%}
%token PLUS TIMES DIV MINUS CONS EQQ MINOR MAJOR MINOREQ MAJOREQ NEQ MOD HD TL UMINUS AND OR NOT SQUARE CUBE ABS ISNULL
%token EOL LQUADRA RQUADRA LPAREN RPAREN FUN TO VAR SEMICOLON EQ LET LETREC IN IF THEN ELSE PRINT
%token <int> INT
%token <string> VAR
%token <bool> BOOL

%right HD TL ISNULL
%right CONS
%right OR
%right AND
%right NOT
%left EQQ MINOR MAJOR MINOREQ MAJOREQ NEQ
%left PLUS MINUS
%left TIMES DIV
%left MOD SQUARE CUBE ABS
%nonassoc UMINUS

%right TO ELSE
%right IN

%type <Ast.root> main
%start main

%%

listexpr
: expr		{ Ast.Cons($1, Ast.Nil) }
| expr SEMICOLON listexpr	{ Ast.Cons ($1,$3) }
;

app
: bfun expr			{ Ast.App ($1,$2) }
| app expr			{ Ast.App ($1,$2) }
| LPAREN app RPAREN	{ $2 }
;

bfun
: VAR				{ Ast.Var $1 }
| NOT expr			{ Ast.App(Ast.Bfun("not"),$2) }
| HD expr			{ Ast.App(Ast.Bfun("hd"),$2) }
| TL expr			{ Ast.App(Ast.Bfun("tl"),$2) }
| ISNULL expr			{ Ast.App(Ast.Bfun("isnull"),$2) }
| SQUARE expr		{ Ast.App(Ast.Bfun("square"),$2) }
| CUBE expr			{ Ast.App(Ast.Bfun("cube"),$2) }
| ABS expr			{ Ast.App(Ast.Bfun("abs"),$2) }
| FUN VAR TO expr 	{ Ast.Fun ($2,$4) }
| LPAREN bfun RPAREN { $2 }
;

expr
: INT					{ Ast.Int $1 }
| BOOL					{ Ast.Bool $1 }
| LPAREN expr RPAREN	{ $2 }
| LPAREN MINUS expr RPAREN %prec UMINUS { Ast.App(Ast.App(Ast.Bfun2("*"),Ast.Int (-1)),$3) }
| expr PLUS expr	{ Ast.App(Ast.App(Ast.Bfun2("+"),$1),$3) }
| expr MINUS expr	{ Ast.App(Ast.App(Ast.Bfun2("-"),$1),$3) }
| expr TIMES expr	{ Ast.App(Ast.App(Ast.Bfun2("*"),$1),$3) }
| expr DIV expr		{ Ast.App(Ast.App(Ast.Bfun2("/"),$1),$3) }
| expr MOD expr		{ Ast.App(Ast.App(Ast.Bfun2("%"),$1),$3) }
| expr EQQ expr		{ Ast.App(Ast.App(Ast.Bfun2("=="),$1),$3) }
| expr NEQ expr		{ Ast.App(Ast.App(Ast.Bfun2("!="),$1),$3) }
| expr MINOR expr	{ Ast.App(Ast.App(Ast.Bfun2("<"),$1),$3) }
| expr MAJOR expr	{ Ast.App(Ast.App(Ast.Bfun2(">"),$1),$3) }
| expr MINOREQ expr	{ Ast.App(Ast.App(Ast.Bfun2("<="),$1),$3) }
| expr MAJOREQ expr	{ Ast.App(Ast.App(Ast.Bfun2(">="),$1),$3) }
| expr AND expr		{ Ast.App(Ast.App(Ast.Bfun2("&&"),$1),$3) }
| expr OR expr		{ Ast.App(Ast.App(Ast.Bfun2("||"),$1),$3) }
| expr CONS expr	{ Ast.Cons ($1,$3) }
| LQUADRA RQUADRA	{ Ast.Nil }
| LQUADRA listexpr RQUADRA 	{ $2 }
| bfun						{ $1 }
| LPAREN app RPAREN			{ $2 }
| LET VAR EQ topexpr IN expr	{ Ast.App(Ast.Fun($2, $6),$4) }
| LETREC VAR EQ topexpr IN expr	{ Ast.App(Ast.Fun($2, $6), Ast.App(y,Ast.Fun($2,$4))) }
| IF expr THEN expr ELSE expr	{ Ast.Cond ($2,$4,$6) }
;


topexpr
: app     { $1 }
| expr    { $1 }
;

main
: topexpr EOL			{ Ast.Exp ($1) }
| LET VAR EQ topexpr EOL	{ Ast.Def ($2,$4) }
| LETREC VAR EQ topexpr EOL	{ Ast.Defrec ($2,$4) }
| PRINT topexpr EOL			{ Ast.Print ($2) }
;
